#pragma once
#include "BaseDefine.h"


namespace WorldWind
{
	/// <summary>
	/// Summary description for Point3d.
	/// </summary>
	typedef struct Point3d
	{
	public:
		 double X, Y, Z;
		// constructors

		Point3d()
			:X(0),Y(0),Z(0)
		{
		}

		D3DXVECTOR3 Vector3()
		{
			return D3DXVECTOR3((float)X, (float)Y, (float)Z);
		}

		D3DXVECTOR4 Vector4()
		{
			return D3DXVECTOR4((float)X, (float)Y, (float)Z, 0);
		}

		Point3d cross(Point3d p)
		{
			return Point3d(
				Y * p.Z - Z * p.Y,
				Z * p.X - X * p.Z,
				X * p.Y - Y * p.X
				);
		}

		double dotProduct(Point3d p)
		{
			return X * p.X + Y * p.Y + Z * p.Z;
		}

		Point3d (double xi, double yi, double zi)	// x,y,z constructor
		{
			X = xi; Y = yi; Z = zi;
		}
		Point3d (const Point3d& P) // copy constructor
		{
			X = P.X;
			Y = P.Y;
			Z = P.Z;
		}

		// assignment operators
		Point3d operator = (Point3d P) // copy operator
		{
			X = P.X; Y = P.Y; Z = P.Z;
			return *this;
		}

		// other operators
		double norm()	// L2 norm
		{
			return sqrt(norm2());
		}

		double norm2() // squared L2 norm
		{
			return X*X + Y*Y + Z*Z;
		}

		Point3d normalize() // normalization
		{
			double n = norm();
			return Point3d(X / n, Y / n, Z / n);
		}

		Point3d operator *= (double k) // multiply by real
		{
			X *= k; Y *= k; Z *= k;
			return *this;
		}

		Point3d operator /= (const double k) // divide by real
		{
			//assert (k != 0.);
			X /= k; Y /= k; Z /= k;
			return *this;
		}

		Point3d operator += (const Point3d & P)	// addition
		{
			X += P.X; Y += P.Y; Z += P.Z;
			return *this;
		}

		inline Point3d & Point3d::operator -= (const Point3d & P) // subtraction
		{
			X -= P.X; Y -= P.Y; Z -= P.Z;
			return *this;
		}

		double Length()
		{
			return sqrt(X * X + Y * Y + Z * Z);
		}

		static Angle GetAngle(Point3d p1, Point3d p2)
		{
			Angle returnAngle;
			returnAngle.Radians = acos(Point3d::dot(p1, p2) / (p1.Length() * p2.Length()));
			return returnAngle;
		}

		friend const Point3d operator+(const Point3d& p1,const Point3d& p2);
		friend const Point3d operator-(const Point3d& p1,const Point3d& p2);
		friend const Point3d operator*(const Point3d& p1,const Point3d& p2);
		friend const Point3d operator/(const Point3d& p1,const Point3d& p2);
		friend bool operator ==(Point3d P1, Point3d P2); // equal?
		friend bool operator !=(Point3d P1, Point3d P2); // equal?
		friend const Point3d operator *(Point3d P, double k);	// multiply by real 2
		friend const  Point3d operator *(double k, Point3d P);	// and its reverse order!
		friend const  Point3d operator /(Point3d P, double k);	// divide by real 2

		// Override the Object.Equals(object o) method:
// 		override bool Equals(object o)
// 		{
// 			try
// 			{
// 				return (bool)(this == (Point3d)o);
// 			}
// 			catch
// 			{
// 				return false;
// 			}
// 		}

		// Override the Object.GetHashCode() method:
		int GetHashCode()
		{
			//not the best algorithm for hashing, but whatever...
			return (int)(X * Y * Z);
		}


// 
		static double dot(Point3d P1, Point3d P2) // inner product 2
		{
			return (P1.X * P2.X + P1.Y * P2.Y + P1.Z * P2.Z);
		}
// 
		Point3d operator *= (const Point3d & P) // cross product
		{
			return (*this = (*this) * P);
		}
// 
		static Point3d cross(Point3d P1, Point3d P2) // cross product
		{
			return P1 * P2;
		}

		// Normal direction corresponds to a right handed traverse of ordered points.
		Point3d unit_normal (Point3d P0, Point3d P1, Point3d P2)
		{
			Point3d p = (P1 - P0) * (P2 - P0);
			double l = p.norm ();
			return Point3d (p.X / l, p.Y / l, p.Z / l);
		}
	}Point3d;
	const Point3d operator+(const Point3d& p1,const Point3d& p2)
	{
		return Point3d(p1.X+p2.X,p1.Y+p2.Y,p1.Z+p2.Z);
	}
	const Point3d operator-(const Point3d& p1,const Point3d& p2)
	{
		return Point3d(p1.X-p2.X,p1.Y-p2.Y,p1.Z-p2.Z);
	}
	const Point3d operator*(const Point3d& p1,const Point3d& p2)
	{
		return Point3d(p1.X*p2.X,p1.Y*p2.Y,p1.Z*p2.Z);
	}
	const Point3d operator/(const Point3d& p1,const Point3d& p2)
	{
		return Point3d(p1.X/p2.X,p1.Y/p2.Y,p1.Z/p2.Z);
	}
	bool operator ==(Point3d P1, Point3d P2) // equal?
	{
		return (P1.X == P2.X && P1.Y == P2.Y && P1.Z == P2.Z);
	}

	bool operator !=(Point3d P1, Point3d P2) // equal?
	{
		return (P1.X != P2.X || P1.Y != P2.Y || P1.Z != P2.Z);
	}
	const Point3d operator *(Point3d P, double k)	// multiply by real 2
	{
		return Point3d (P.X * k, P.Y * k, P.Z * k);
	}

	 const  Point3d operator *(double k, Point3d P)	// and its reverse order!
	{
		return Point3d (P.X * k, P.Y * k, P.Z * k);
	}

	 const  Point3d operator /(Point3d P, double k)	// divide by real 2
	{
		return Point3d (P.X / k, P.Y / k, P.Z / k);
	}
}
